package com.bytz.modules.cms.auditBatch.service.direct;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bytz.common.exception.BytzBootException;
import com.bytz.common.system.query.QueryGenerator;
import com.bytz.common.util.mentity.EntityMergeInfoV2;
import com.bytz.common.util.mentity.util.MergeEntityOptions;
import com.bytz.common.util.mentity.util.MergeEntityUtil;
import com.bytz.modules.cms.auditBatch.service.IAdminProductChangeService;
import com.bytz.modules.cms.product.constant.ChangeConstants;
import com.bytz.modules.cms.product.entity.Product;
import com.bytz.modules.cms.product.entity.ProductChange;
import com.bytz.modules.cms.product.mapper.ProductChangeMapper;
import com.bytz.modules.cms.product.service.IProductService;
import com.bytz.modules.cms.user.constants.AuditBatchConstants;
import com.bytz.modules.cms.user.service.IAuditBatchService;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AdminProductChangeServiceImpl extends ServiceImpl<ProductChangeMapper, ProductChange> implements IAdminProductChangeService {

    @Autowired
    private IProductService productService;

    @Autowired
    private IAuditBatchService auditBatchService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncChangeByBatchId(String batchId) {

        List<ProductChange> list = getByBatchId(batchId);

        if (CollectionUtil.isEmpty(list)) {
            return;
        }

        Map<String, List<ProductChange>> idGroup = list.stream()
                .collect(Collectors.groupingBy(ProductChange::getProductId));

        List<Product> updateList = idGroup.entrySet().stream().map(entry -> {
            String id = entry.getKey();
            List<ProductChange> values = entry.getValue();
            Product updateEntity = new Product();
            updateEntity.setId(id);

            values.stream().findFirst().ifPresent(productChange -> {

                if (ChangeConstants.ProductChangeType.PRICE_ADJUSTMENT.equals(productChange.getType())) {
                    updateEntity.setBasePrice(productChange.getBasePrice());
                    updateEntity.setEffectTime(LocalDateTime.now());
                }

                if (ChangeConstants.ProductChangeType.OTHER_INFO_ADJUSTMENT.equals(productChange.getType())) {
                    updateEntity.setDeliveryDate(productChange.getDeliveryDate());
                    updateEntity.setMaxOrderQuantity(productChange.getMaxOrderQuantity());
                    updateEntity.setCostPrice(productChange.getCostPrice());
                    updateEntity.setStatus(productChange.getStatus());
                    updateEntity.setEffectTime(LocalDateTime.now());
                }
            });
            return updateEntity;
        }).collect(Collectors.toList());

        updateSyncData(batchId, updateList);
    }

    /**
     * 根据batchId获取未同步的change
     */
    private List<ProductChange> getByBatchId(String batchId) {
        return lambdaQuery()
                .eq(ProductChange::getAuditBatchId, batchId)
                .eq(ProductChange::getSyncStatus, AuditBatchConstants.SyncStatus.WAITING_SYNC)
                .orderByDesc(ProductChange::getCreateTime)
                .list();
    }

    /**
     * 更改auditBatch表 将对应id设置为已同步
     * 更改change表 对应batchId设置为已同步
     * 更新对应的主表
     */
    private void updateSyncData(String batchId, List<Product> updateList) {

        productService.updateBatchById(updateList);

        lambdaUpdate()
                .in(ProductChange::getAuditBatchId, batchId)
                .set(ProductChange::getSyncStatus, AuditBatchConstants.SyncStatus.SYNCED)
                .update();

        auditBatchService.setStatusSynced(batchId);
    }

    @Override
    public IPage<ProductChange> queryPage(Page<ProductChange> page, ProductChange change, Map<String, String[]> parameterMap) {
        QueryWrapper<ProductChange> queryWrapper = QueryGenerator.initQueryWrapper(change, parameterMap);
        return page(page, queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = BytzBootException.class)
    public void importExcel(String batchId, List<ProductChange> list) throws Exception {
        //商品重复检测
        Set<String> productChangeNums = checkProductNumMulti(list);
        EntityMergeInfoV2<ProductChange> mergeInfo = MergeEntityUtil.calculateEntityMergeInfo(list,
                getByBatchId(batchId),
                MergeEntityOptions.<ProductChange>builder()
                        .keyExtractor(ProductChange::getProductNumber)
                        .updateEntityMerger((k1, k2) -> {
                            k2.setBasePrice(k1.getBasePrice());
                            return k2;
                        })
                        .build());

        List<Product> products = productService.getByProductNumbers(productChangeNums);
        Map<String, Product> productMap = products.stream()
                .collect(Collectors.toMap(Product::getProductNumber, Function.identity()));
        // 商品存在检测
        checkProductExist(productChangeNums, productMap.keySet());

        List<ProductChange> newAdd = mergeInfo.getAddEntities().stream().map(change -> {
            Product product = productMap.get(change.getProductNumber());
            if (product == null) {
                return null;
            }
            BeanUtil.copyProperties(product, change,
                    LambdaUtil.getFieldName(ProductChange::getBasePrice),
                    LambdaUtil.getFieldName(ProductChange::getId)
            );
            change.setProductId(product.getId());
            change.setAuditBatchId(batchId);
            change.setType(ChangeConstants.ProductChangeType.PRICE_ADJUSTMENT);
            return change;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        mergeInfo.setAddEntities(newAdd);
        mergeInfo.setDeleteEntities(Collections.emptyList());
        MergeEntityUtil.doDbMerge(mergeInfo, this);
    }

    @Override
    public boolean checkChangeData(String id) {
        List<ProductChange> productChanges = getByBatchId(id);
        Set<String> productChangeNumbers = checkProductNumMulti(productChanges);
        List<Product> products = productService.getByProductNumbers(productChangeNumbers);
        Set<String> productNums = products.stream().map(Product::getProductNumber).collect(Collectors.toSet());
        checkProductExist(productChangeNumbers, productNums);
        return true;
    }

    /**
     *
     * @param productChangeNumbers 需要变更的
     * @param productNums 数据库中的
     */
    private static void checkProductExist(Set<String> productChangeNumbers, Set<String> productNums) {
        if ( productNums.size() != productChangeNumbers.size()) {
            String procNums = productChangeNumbers.stream().filter(productChangeNumber -> !productNums.contains(productChangeNumber)).collect(Collectors.joining(","));
            throw new BytzBootException("商品不存在:" + procNums);
        }
    }


    private static Set<String> checkProductNumMulti(List<ProductChange> productChanges) {
        HashMap<String, Integer> productChangeNumbers = productChanges.stream().map(ProductChange::getProductNumber).collect(Collectors.toMap(Function.identity(), v -> 1, Integer::sum, HashMap::new));

        if (productChanges.size() != productChangeNumbers.size()) {

            String multiNum = productChangeNumbers.entrySet().stream()
                    .filter(Objects::nonNull)
                    .filter(entry -> entry.getValue() != null)
                    .filter(entry -> entry.getValue() > 1)
                    .map(Map.Entry::getKey)
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.joining(","));
            throw new BytzBootException("商品编号不能重复:" + multiNum);
        }
        return productChangeNumbers.keySet();
    }

    @Override
    public void updateEffectTimeByBatchId(String id, LocalDateTime effectTime) {
        lambdaUpdate()
                .eq(ProductChange::getAuditBatchId, id)
                .set(ProductChange::getEffectTime, effectTime)
                .update();
    }

    @Override
    @Transactional(rollbackFor = BytzBootException.class)
    public String saveChange(ProductChange change) throws Exception {
        boolean exists = lambdaQuery()
                .eq(ProductChange::getProductNumber, change.getProductNumber())
                .eq(ProductChange::getAuditBatchId, change.getAuditBatchId())
                .exists();

        if (exists) {
            throw new BytzBootException("商品编号已经存在");
        }
        save(change);
        return "保存成功";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByBatchId(String id) {
        lambdaUpdate().eq(ProductChange::getAuditBatchId, id).remove();
    }

    @Override
    public void importOtherExcel(String batchId, List<ProductChange> list) {
        //商品重复检测
        Set<String> productChangeNums = checkProductNumMulti(list);
        EntityMergeInfoV2<ProductChange> mergeInfo = MergeEntityUtil.calculateEntityMergeInfo(list,
                getByBatchId(batchId),
                MergeEntityOptions.<ProductChange>builder()
                        .keyExtractor(ProductChange::getProductNumber)
                        .updateEntityMerger((update, db) -> {
                            update.setId(db.getId());
                            return update;
                        })
                        .build());

        List<Product> products = productService.getByProductNumbers(productChangeNums);
        Map<String, Product> productMap = products.stream()
                .collect(Collectors.toMap(Product::getProductNumber, Function.identity()));
        // 商品存在检测
        checkProductExist(productChangeNums, productMap.keySet());

        List<ProductChange> newAdd = mergeInfo.getAddEntities().stream().map(add -> {
            Product product = productMap.get(add.getProductNumber());
            if (product == null) {
                return null;
            }
            add.setProductId(product.getId());
            add.setAuditBatchId(batchId);
            add.setType(ChangeConstants.ProductChangeType.OTHER_INFO_ADJUSTMENT);
            return add;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        mergeInfo.setAddEntities(newAdd);
        mergeInfo.setDeleteEntities(Collections.emptyList());
        MergeEntityUtil.doDbMerge(mergeInfo, this);
    }
}